//
//  LyAppDelegate.m
//  Libert3D
//
//  Created by Richard Houle on 12-12-03.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "LyAppDelegate.h"

#include <Liberty3D/Liberty3D.h>
#include <Liberty3D/Engine/Context.h>
#include <Liberty3D/Engine/Game.h>
#include <Liberty3D/Engine/Performance.h>

#include <libkern/OSAtomic.h>

#include <pthread.h>

using namespace Liberty::Engine;

namespace Liberty { namespace Engine { namespace Mac {

    class SourceInfo {
    public:
        volatile bool             _running;
        volatile bool             _stopping;
        int32_t                   _count;
        std::shared_ptr<Context>  _context;
        pthread_t                 _thread;
        CFRunLoopRef              _runLoop;
        CFRunLoopSourceRef        _source;
        
    public:
        SourceInfo(const std::shared_ptr<Context>& context) : _running(false), _stopping(false), _count(0), _context(context) {
        }
        
        ~SourceInfo() {
            Game::stoping(_context);
            _stopping = true;
            
            while (_running) {
                usleep(10);
            }
            
            CFRelease(_runLoop);
            Game::stoped(_context);
        }
        
    public:
        static const void* retain(SourceInfo* v) {
            OSAtomicIncrement32(&(v->_count));
            return v;
        }
        
        static void release(SourceInfo* v) {
            if (OSAtomicDecrement32(&(v->_count)) == 0) {
                delete v;
            }
        }

        static CFStringRef description(SourceInfo* v) {
            return CFStringCreateWithCString(NULL, "Liberty::Engine::Mac::Source", kCFStringEncodingUTF8);
        }

        static Boolean equal(SourceInfo* o1, SourceInfo* o2) {
            return o1 == o2;
        }

        static CFHashCode hash(SourceInfo* o1) {
            return (CFHashCode)o1;
        }

        static void tick(SourceInfo* info) {
            Performance::Counter nextTick;
            
            info->_context->tick(nextTick);
        }
        
        static void* thread(SourceInfo* info) {
            info->_running = true;

            Performance::Counter nextTick;
            
            Performance::query(nextTick);
            
            while (!info->_stopping) {
                CFRunLoopSourceSignal(info->_source);
                CFRunLoopWakeUp(info->_runLoop);

                mach_wait_until(nextTick);
                nextTick += info->_context->fixedDelta();
            }
            
            info->_running = false;
            return NULL;
        }
    };

}}}

using namespace Liberty::Engine::Mac;

@implementation LyAppDelegate

- (CFRunLoopSourceRef)lyAddContextToLoop:(const std::shared_ptr<Context>&)context {
    CFRunLoopRef runLoop = CFRunLoopGetCurrent();
    
    CFRetain(runLoop);
    
    SourceInfo*            sourceInfo = new SourceInfo(context);
    CFRunLoopSourceContext sourceContext;
    
    memset(&sourceContext, 0, sizeof(sourceContext));
    sourceContext.info            = sourceInfo;
    sourceContext.retain          = (CFAllocatorRetainCallBack)SourceInfo::retain;
    sourceContext.release         = (CFAllocatorReleaseCallBack)SourceInfo::release;
    sourceContext.copyDescription = (CFStringRef (*)(const void*))SourceInfo::description;
    sourceContext.equal           = (Boolean (*)(const void*, const void*))SourceInfo::equal;
    sourceContext.hash            = (CFHashCode (*)(const void*))SourceInfo::hash;
    sourceContext.perform         = (void (*)(void*))SourceInfo::tick;
    
    CFRunLoopSourceRef sourceRef = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &sourceContext);
    
    sourceInfo->_source  = sourceRef;
    sourceInfo->_runLoop = runLoop;

    CFRunLoopAddSource(runLoop, sourceRef, kCFRunLoopCommonModes);

    pthread_attr_t threadAttr;
    
    pthread_attr_init(&threadAttr);
    pthread_create(&sourceInfo->_thread, &threadAttr, (void*(*)(void*))SourceInfo::thread, sourceInfo);
    pthread_attr_destroy(&threadAttr);
    
    CFRelease(runLoop);
    return sourceRef;
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    Game::init();
    
    std::shared_ptr<Context> context(new Context());
    
    Game::starting(context);
    _source = [self lyAddContextToLoop:context];
    Game::started(context);
}

- (void)applicationWillTerminate:(NSNotification*)aNotification {
    if (_source) {
        CFRunLoopSourceInvalidate(_source);
        CFRelease(_source);
        _source = NULL;
    }
}

@end
